#include "LecteurPhraseAvecArbre.h"

#include <stdlib.h>
#include <iostream>
using namespace std;

////////////////////////////////////////////////////////////////////////////////
LecteurPhraseAvecArbre::LecteurPhraseAvecArbre(string nomFich) :
	ls(nomFich), ts() {
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::analyse() {
	arbre=programme();
	cout << "Syntaxe correcte." << endl;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::programme() {
	// <programme> ::= debut <seq_inst> fin FIN_FICHIER

	sauterSymCour("debut");
	Noeud* si = seqInst();
	sauterSymCour("fin");
	testerSymCour("<FINDEFICHIER>");
	return si;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::seqInst() {
//<seqInst> ::= <inst> ; { <inst> ; }

	NoeudSeqInst* si = new NoeudSeqInst();
	do {
		si->ajouteInstruction(inst());
		sauterSymCour(";");
	} while((ls.getSymCour()=="<VARIABLE>") || 
                (ls.getSymCour()=="si") || 
                (ls.getSymCour()=="tantque") || 
                (ls.getSymCour()=="repeter") || 
                (ls.getSymCour()=="pour") || 
                (ls.getSymCour()=="lire") || 
                (ls.getSymCour()=="ecrire") || 
                (ls.getSymCour()=="switch"));
	// tant que le symbole courant est un debut possible d'instruction...
	return si;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::inst() {
// <inst> ::= <affectation> | <instRepeter> | <instTq> | <instSi> | | <instLire> | <instEcrire> | <instPour>
    
        if (ls.getSymCour()=="si"){
             return instSi();
        }
        else if (ls.getSymCour()=="tantque"){
             return instTq();
        }
        else if (ls.getSymCour()=="repeter"){
             return instRepeter();
        }
        else if (ls.getSymCour()=="pour"){
             return instPour();
        }
        else if (ls.getSymCour()=="lire"){
             return instLire();
        }
        else if (ls.getSymCour()=="ecrire"){
             return instEcrire();
        }
        else if (ls.getSymCour()=="switch"){
             return instSwitch();
        }
        else{
            return affectation();
        }
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instSi(){
//<instSi> ::= si ( <expBool> ) <seqInst> { sinonsi (<expBool>) <seqInst> }[sinon <seqInst> ] finsi  
        NoeudInstSi * n = new NoeudInstSi();
        sauterSymCour("si");
        sauterSymCour("(");
        Noeud * cond = expBool();
        sauterSymCour(")");
        Noeud * seqinst = seqInst();
        n->ajouteCondition(cond,seqinst);
        while (ls.getSymCour()=="sinonsi"){
            sauterSymCour("sinonsi");
            sauterSymCour("(");
            Noeud * cond = expBool();
            sauterSymCour(")");
            Noeud * seq = seqInst();
            n->ajouteCondition(cond,seq);
        }
        if (ls.getSymCour()=="sinon"){
            sauterSymCour("sinon");
            Noeud * seqinst = seqInst();
            n->ajouteCondition(NULL,seqinst);
        }
       sauterSymCour("finsi");
       return n;    
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instTq(){
//<instTq> ::=tantque (<expBool> ) <seqInst> fintantque
        sauterSymCour("tantque");
        sauterSymCour("(");
        Noeud* cond = expBool();
        sauterSymCour(")");
        Noeud* seqinst = seqInst();
        sauterSymCour("fintantque"); 
        return new NoeudInstTq(seqinst, cond);
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instRepeter(){
//<instRepeter> ::= repeter <seqInst> jusqua (<expBool> ) 
        sauterSymCour("repeter");
        Noeud* seqinst = seqInst();
        sauterSymCour("jusqua");
        sauterSymCour("(");
        Noeud* cond = expBool();
        sauterSymCour(")");  
        return new NoeudInstRepeter(seqinst,cond);
}

//////////////////////////////////////////////////////////////////////////////// 
Noeud* LecteurPhraseAvecArbre::instPour(){
//<instPour> ::=pour (<affectation>; <expBool>; <affectation> ) <seqInst> finpour
        sauterSymCour("pour");
        sauterSymCour("(");
        Noeud* depart = affectation();
        sauterSymCour(";");
        Noeud* cond = expBool();
        sauterSymCour(";");
        Noeud* arrive = affectation();
        sauterSymCour(")");
        Noeud* seqinst = seqInst();
        sauterSymCour("finpour"); 
        return new NoeudInstPour(seqinst,depart,cond,arrive);
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instLire(){   
// <instLire> ::=lire(<variable>)
        sauterSymCour("lire");
        sauterSymCour("(");
        //sauterSymCour("<VARIABLE>");
        testerSymCour("<VARIABLE>");
	Noeud* var = ts.chercheAjoute(ls.getSymCour());
        ls.suivant();
        sauterSymCour(")"); 
        return new NoeudInstLire(var);
}

////////////////////////////////////////////////////////////////////////////////     
Noeud* LecteurPhraseAvecArbre::instEcrire(){  
// <instEcrire> ::=ecrire(<expression> | <chaine>)
        Noeud* var = NULL;
        sauterSymCour("ecrire");
        sauterSymCour("(");
        if (ls.getSymCour()=="<CHAINE>"){
           //sauterSymCour("<CHAINE>");
           testerSymCour("<CHAINE>");
           var=ts.chercheAjoute(ls.getSymCour());
           var=new NoeudInstEcrire(true, var);
           ls.suivant();
        }else{
           var=new NoeudInstEcrire(false, expression());
        }
        sauterSymCour(")"); 
        return var;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instSwitch(){  
//switch ( <variable> ) {case <entier> : <seqinst> break;} default : <seqinst> break
//default:  si <variable> n'est égale à aucune variable  break;}
        NoeudInstSwitch * n = new NoeudInstSwitch();
        sauterSymCour("switch");
        sauterSymCour("(");
        //sauterSymCour("<VARIABLE>");
        testerSymCour("<VARIABLE>");
	Noeud* var = ts.chercheAjoute(ls.getSymCour());
        ls.suivant();
        sauterSymCour(")");
        while (ls.getSymCour()=="case"){
            sauterSymCour("case");
            //sauterSymCour("<ENTIER>");
            testerSymCour("<ENTIER>");
            Noeud* cas = new NoeudOperateurBinaire(Symbole("=="),var,ts.chercheAjoute(ls.getSymCour()));
            ls.suivant();
            sauterSymCour(":");
            Noeud * seqinst = seqInst();
            sauterSymCour("break"); 
            sauterSymCour(";");
            n->ajouteCondition(cas,seqinst);
        }
        sauterSymCour("default"); 
        sauterSymCour(":");
        Noeud * seqinst = seqInst();
        sauterSymCour("break"); 
        n->ajouteCondition(NULL,seqinst);
        
        return n;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::affectation() {
// <affectation> ::= <variable> = <expression>
        Noeud* exp = NULL;
	testerSymCour("<VARIABLE>");
	Noeud* var = ts.chercheAjoute(ls.getSymCour());
	ls.suivant();
        if (ls.getSymCour()=="="){
                sauterSymCour("=");
                exp = expression();
        }else if (ls.getSymCour()=="++"){
                sauterSymCour("++");
                exp = new NoeudOperateurBinaire(Symbole("+"),var,ts.chercheAjoute(Symbole("1")));
        }else if (ls.getSymCour()=="--"){
                sauterSymCour("--");
                exp = new NoeudOperateurBinaire(Symbole("-"),var,ts.chercheAjoute(Symbole("1")));
        }else if (ls.getSymCour()=="+="){
                sauterSymCour("+=");
                exp = new NoeudOperateurBinaire(Symbole("+"),var,expression());
        }else if (ls.getSymCour()=="-="){
                sauterSymCour("-=");
                exp = new NoeudOperateurBinaire(Symbole("-"),var,expression());
        }else if (ls.getSymCour()=="*="){
                sauterSymCour("*=");
                exp = new NoeudOperateurBinaire(Symbole("*"),var,expression());
        }else if (ls.getSymCour()=="/="){
                sauterSymCour("/=");
                exp = new NoeudOperateurBinaire(Symbole("/"),var,expression());
        }
	return new NoeudAffectation(var,exp);
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::expression() {
// <expression> ::= <terme> { <opAdd> <terme> }

	Noeud* fact = terme();
	while (ls.getSymCour()=="+" || ls.getSymCour()=="-") {
		Symbole operateur = opAdd(); // on stocke le symbole de l'opérateur
		Noeud* factDroit=terme(); // lecture de l'operande droit
		fact = new NoeudOperateurBinaire(operateur,fact,factDroit); // const. du noeud
	}
	return fact;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::terme() {
// <terme> ::= <facteur> { <opMult> facteur> }

	Noeud* fact = facteur();
        while (ls.getSymCour()=="*" || ls.getSymCour()=="/" || ls.getSymCour()=="%") {
                Symbole operateur = opMult();
                Noeud* factDroit=facteur();
                fact = new NoeudOperateurBinaire(operateur,fact,factDroit);
        }
        return fact;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::facteur() {
//<facteur> ::= <ENTIER> | <VARIABLE> | <CHAINE> | <opUnaire> <expBool> | ( <expBool> )

	Noeud* fact=NULL;

	if (ls.getSymCour()=="<VARIABLE>" || ls.getSymCour()=="<ENTIER>" || ls.getSymCour()=="<CHAINE>") {
		fact=ts.chercheAjoute(ls.getSymCour());
		ls.suivant();
	} else if (ls.getSymCour()=="-" || ls.getSymCour()=="non") {
                Symbole operateur = opUnaire();
                fact = new NoeudOperateurUnaire(operateur, expBool());
	} else if (ls.getSymCour()=="(") {
		ls.suivant();
		fact=expBool();
		sauterSymCour(")");
	} else
		erreur("<facteur>");
	return fact;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opAdd()
// <opAdd> ::= + | -
{
        Symbole operateur;
        if (ls.getSymCour()=="+" || ls.getSymCour()=="-"){
                operateur=ls.getSymCour();
		ls.suivant();
	}else
		erreur("<opAdd>");
        return operateur;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opMult()
// <opMult> ::= * | /
{
        Symbole operateur;
	if (ls.getSymCour()=="*" || ls.getSymCour()=="/" || ls.getSymCour()=="%"){
                operateur=ls.getSymCour();
		ls.suivant();
        }else
		erreur("<opMult>");
        return operateur;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::expBool()
//     <expBool> ::= <expBoolEt> { <opBoolOu> <expBoolEt> }
{
        Noeud* fact = expBoolEt();
        while (ls.getSymCour()=="ou") {
                Symbole operateur = opBoolOu();
                Noeud* factDroit=expBoolEt();
                fact = new NoeudOperateurBinaire(operateur,fact,factDroit);
        }
        return fact;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::expBoolEt()
 //     <expBoolEt> ::= <relation> { <opBoolEt> <relation> }
{
        Noeud* fact = relation();
        while (ls.getSymCour()=="et") {
                Symbole operateur = opBoolEt();
                Noeud* factDroit=relation();
                fact = new NoeudOperateurBinaire(operateur,fact,factDroit);
        }
        return fact;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opBoolEt()
//  <opBoolEt> ::= et  
{
        Symbole operateur;
	if (ls.getSymCour()=="et"){
                operateur=ls.getSymCour();
		ls.suivant();
	}else
		erreur("<opBoolEt>");
        return operateur;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opBoolOu()
//  <opBool> ::= ou 
{
        Symbole operateur;
	if (ls.getSymCour()=="ou"){
                operateur=ls.getSymCour();
		ls.suivant();
	}else
		erreur("<opBoolOu>");
        return operateur;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::relation()
// <relation> ::= <expression> { <opRel> <expression> }
{
	Noeud* fact = expression();
        while (ls.getSymCour()=="==" || ls.getSymCour()=="!=" || ls.getSymCour()=="<" || ls.getSymCour()=="<=" || ls.getSymCour()==">" || ls.getSymCour()==">=") {
                Symbole operateur = opRel();
                Noeud* factDroit=expression();
                fact = new NoeudOperateurBinaire(operateur,fact,factDroit);
        }
        return fact;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opRel()
//<opRel> ::= == | != | < | <= | > | >=
{
        Symbole operateur;
	if (ls.getSymCour()=="==" || ls.getSymCour()=="!=" || ls.getSymCour()=="<" || ls.getSymCour()=="<=" || ls.getSymCour()==">" || ls.getSymCour()==">="){
		operateur=ls.getSymCour();
                ls.suivant();
        }else
		erreur("<opRel>");
        return operateur;
}


////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opUnaire()
// <opUnaire> ::= - | non 
{
        Symbole operateur;
	if (ls.getSymCour()=="-" || ls.getSymCour()=="non"){
		operateur=ls.getSymCour();
                ls.suivant();
        }else
		erreur("<opUnaire>");
        return operateur;
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::testerSymCour(string ch) {
	if (ls.getSymCour() != ch) {
		cout << endl << "-------- Erreur ligne " << ls.getLigne()
				<< " - Colonne " << ls.getColonne() << endl << "   Attendu : "
				<< ch << endl << "   Trouve  : " << ls.getSymCour() << endl
				<< endl;
		exit(0); // plus tard, on levera une exception
	}
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::sauterSymCour(string ch) {
	testerSymCour(ch);
	ls.suivant();
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::erreur(string mess) {
	cout << endl << "-------- Erreur ligne " << ls.getLigne() << " - Colonne "
			<< ls.getColonne() << endl << "   Attendu : " << mess << endl
			<< "   Trouve  : " << ls.getSymCour() << endl << endl;
	exit(0); // plus tard, on levera une exception
}